"""
推荐系统路由
"""
from flask import request, jsonify
from flask_jwt_extended import jwt_required, get_jwt_identity
from app.blueprints.recommend import recommend_bp
from app.models.user import User
from app.models.course import Course
from app.models.student_course import StudentCourse
from app.models.recommendation import RecommendationFeedback
from app.extensions import db
from collections import defaultdict
import math


def calculate_user_similarity(user_courses, target_courses):
    """计算用户相似度（Jaccard系数）"""
    if not user_courses or not target_courses:
        return 0.0
    
    intersection = len(user_courses.intersection(target_courses))
    union = len(user_courses.union(target_courses))
    
    return intersection / union if union > 0 else 0.0


def user_based_collaborative_filtering(user_id, n_similar_users=10, n_recommendations=10):
    """基于用户的协同过滤推荐

    返回: dict[course_id] -> 聚合相似度得分
    """
    try:
        # 获取当前用户的 student_profile
        from app.models.user import StudentProfile
        user = User.query.get(user_id)
        if not user or not user.student_profile:
            return []
        
        student_profile_id = user.student_profile.id
        
        # 获取所有学生的选课记录
        all_student_courses = db.session.query(
            StudentCourse.student_id,
            StudentCourse.course_id
        ).all()
        
        # 构建学生-课程字典 (使用 student_profile.id)
        student_course_dict = defaultdict(set)
        for sc in all_student_courses:
            student_course_dict[sc.student_id].add(sc.course_id)
        
        target_courses = student_course_dict.get(student_profile_id, set())
        
        if not target_courses:
            return {}
        
        # 计算与其他学生的相似度
        similarities = {}
        for other_student_id, other_courses in student_course_dict.items():
            if other_student_id == student_profile_id:
                continue
            
            similarity = calculate_user_similarity(other_courses, target_courses)
            if similarity > 0:
                similarities[other_student_id] = similarity
        
        # 找到最相似的N个学生
        sorted_similar_students = sorted(
            similarities.items(),
            key=lambda x: x[1],
            reverse=True
        )[:n_similar_users]
        
        # 聚合推荐课程
        recommendations = defaultdict(float)
        for similar_student_id, similarity in sorted_similar_students:
            for course_id in student_course_dict[similar_student_id]:
                if course_id not in target_courses:
                    recommendations[course_id] += similarity
        
        # 排序并返回Top-N
        sorted_recommendations = sorted(
            recommendations.items(),
            key=lambda x: x[1],
            reverse=True
        )[:n_recommendations]
        # 转为dict
        return {course_id: score for course_id, score in sorted_recommendations}
        
    except Exception as e:
        print(f"Error in collaborative filtering: {str(e)}")
        return []


def get_popular_courses(limit=10, exclude_courses=None):
    """获取热门课程

    返回: dict[course_id] -> 选课人数(受限于limit之前的真实统计)
    """
    try:
        if exclude_courses is None:
            exclude_courses = set()
        
        # 统计每门课程的选课人数
        popular = db.session.query(
            StudentCourse.course_id,
            db.func.count(StudentCourse.id).label('count')
        ).group_by(StudentCourse.course_id).order_by(
            db.desc('count')
        ).all()
        
        # 排除已选课程
        recommendations = []
        for course_id, cnt in popular:
            if course_id in exclude_courses:
                continue
            recommendations.append((course_id, int(cnt)))
            if len(recommendations) >= limit:
                break
        
        return {cid: c for cid, c in recommendations}
        
    except Exception as e:
        print(f"Error getting popular courses: {str(e)}")
        return []


@recommend_bp.route('/users/<int:user_id>', methods=['GET'])
@jwt_required()
def get_recommendations(user_id):
    """获取用户个性化推荐"""
    try:
        current_user_id = int(get_jwt_identity())
        
        # 验证权限（只能查看自己的推荐或管理员可查看所有）
        current_user = User.query.get(current_user_id)
        if current_user.role != 'admin' and current_user_id != user_id:
            return jsonify({
                'success': False,
                'error': 'Permission denied'
            }), 403
        
        user = User.query.get(user_id)
        if not user or user.role != 'student':
            return jsonify({
                'success': False,
                'error': 'Student not found'
            }), 404
        
        # 获取学生已选课程
        student_courses = StudentCourse.query.filter_by(student_id=user_id).all()
        enrolled_courses = set([sc.course_id for sc in student_courses])
        
        # 协同过滤推荐（带分数）
        cf_scores = user_based_collaborative_filtering(
            user_id,
            n_similar_users=10,
            n_recommendations=15
        )
        
        # 热门课程推荐（作为补充）
        popular_scores = get_popular_courses(
            limit=10,
            exclude_courses=enrolled_courses
        )
        
        # 计算相关性分数：协同过滤权重0.7 + 热度归一化0.3
        combined_scores = {}
        max_pop = max(popular_scores.values()) if popular_scores else 1
        for cid, s in cf_scores.items():
            if cid in enrolled_courses:
                continue
            combined_scores[cid] = combined_scores.get(cid, 0.0) + 0.7 * float(s)
        for cid, cnt in popular_scores.items():
            if cid in enrolled_courses:
                continue
            popularity = float(cnt) / float(max_pop)
            combined_scores[cid] = combined_scores.get(cid, 0.0) + 0.3 * popularity
        
        # 允许前端选择只看未学过/冲突检测
        only_unenrolled = request.args.get('only_unenrolled', 'true').lower() == 'true'
        enable_conflict_check = request.args.get('conflict_check', 'false').lower() == 'true'
        day = request.args.get('day', type=int)
        start_slot = request.args.get('start_slot', type=int)
        end_slot = request.args.get('end_slot', type=int)
        
        # 过滤列表
        final_ids = [cid for cid, _ in sorted(combined_scores.items(), key=lambda x: x[1], reverse=True)]
        if only_unenrolled:
            final_ids = [cid for cid in final_ids if cid not in enrolled_courses]
        
        if enable_conflict_check:
            # 解析课程的时间标签：dow-<n> 与 slot-<start>-<end>
            def parse_slots(course: Course):
                d = None
                s = None
                e = None
                try:
                    for tag in course.tags:
                        t = tag.tag or ''
                        if t.startswith('dow-'):
                            d = int(t.split('-', 1)[1])
                        elif t.startswith('slot-'):
                            parts = t.split('-')
                            if len(parts) == 3:
                                s = int(parts[1])
                                e = int(parts[2])
                except Exception:
                    pass
                if d is None or s is None or e is None:
                    return None
                return (d, s, e)

            # 获取现有课程的时段
            existing_courses = Course.query.filter(Course.id.in_(list(enrolled_courses))).all()
            existing_slots = []
            for c in existing_courses:
                slot = parse_slots(c)
                if slot:
                    existing_slots.append(slot)

            # 如果提供了特定时间段，则将该时间段加入冲突参照
            if day and start_slot and end_slot:
                existing_slots.append((day, start_slot, end_slot))

            def is_conflict(course: Course) -> bool:
                slot = parse_slots(course)
                if not slot:
                    return False
                cd, cs, ce = slot
                for d, s, e in existing_slots:
                    if cd != d:
                        continue
                    if not (ce < s or cs > e):
                        return True
                return False

            filtered_ids = []
            for cid in final_ids:
                c = Course.query.get(cid)
                if c and is_conflict(c):
                    continue
                filtered_ids.append(cid)
            final_ids = filtered_ids
        
        # 获取课程详细信息
        recommended_courses = []
        for course_id in final_ids[:20]:
            course = Course.query.get(course_id)
            if course:
                course_data = course.to_dict()
                
                # 添加推荐理由
                if course_id in cf_scores:
                    course_data['recommendation_reason'] = '基于相似学生的选课偏好推荐'
                    course_data['recommendation_type'] = 'collaborative_filtering'
                else:
                    course_data['recommendation_reason'] = '热门课程推荐'
                    course_data['recommendation_type'] = 'popular'
                # 附加后端相关性分数
                course_data['relevance_score'] = round(float(combined_scores.get(course_id, 0.0)), 4)
                
                recommended_courses.append(course_data)
        
        return jsonify({
            'success': True,
            'data': {
                'user_id': user_id,
                'recommendations': recommended_courses,
                'total': len(recommended_courses)
            }
        }), 200
        
    except Exception as e:
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500


@recommend_bp.route('/feedback', methods=['POST'])
@jwt_required()
def submit_feedback():
    """提交推荐反馈"""
    try:
        current_user_id = int(get_jwt_identity())
        data = request.get_json()
        
        # 验证必填字段
        required_fields = ['course_id', 'feedback_type']
        for field in required_fields:
            if field not in data:
                return jsonify({
                    'success': False,
                    'error': f'Missing required field: {field}'
                }), 400
        
        # 验证课程存在
        course = Course.query.get(data['course_id'])
        if not course:
            return jsonify({
                'success': False,
                'error': 'Course not found'
            }), 404
        
        # 创建反馈记录
        feedback = RecommendationFeedback(
            user_id=current_user_id,
            course_id=data['course_id'],
            recommendation_id=data.get('recommendation_id'),
            feedback_type=data['feedback_type'],
            rating=data.get('rating'),
            comment=data.get('comment')
        )
        
        db.session.add(feedback)
        db.session.commit()
        
        return jsonify({
            'success': True,
            'message': 'Feedback submitted successfully',
            'data': feedback.to_dict()
        }), 201
        
    except Exception as e:
        db.session.rollback()
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500


@recommend_bp.route('/courses/<int:course_id>/similar', methods=['GET'])
@jwt_required()
def get_similar_courses(course_id):
    """获取相似课程"""
    try:
        course = Course.query.get(course_id)
        if not course:
            return jsonify({
                'success': False,
                'error': 'Course not found'
            }), 404
        
        # 基于标签和院系查找相似课程
        similar_courses = []
        
        # 1. 相同院系的课程
        dept_courses = Course.query.filter(
            Course.department == course.department,
            Course.id != course_id
        ).limit(5).all()
        
        for c in dept_courses:
            course_data = c.to_dict()
            course_data['similarity_reason'] = '相同院系'
            similar_courses.append(course_data)
        
        # 2. 相同标签的课程
        course_tags = set([tag.tag for tag in course.tags])
        if course_tags:
            for tag in course_tags:
                tag_courses = db.session.query(Course).join(
                    Course.tags
                ).filter(
                    Course.id != course_id,
                    Course.id.notin_([c['id'] for c in similar_courses])
                ).limit(3).all()
                
                for c in tag_courses:
                    course_data = c.to_dict()
                    course_data['similarity_reason'] = f'相同标签: {tag}'
                    similar_courses.append(course_data)
        
        # 限制返回数量
        similar_courses = similar_courses[:10]
        
        return jsonify({
            'success': True,
            'data': {
                'course': course.to_dict(),
                'similar_courses': similar_courses,
                'total': len(similar_courses)
            }
        }), 200
        
    except Exception as e:
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500


@recommend_bp.route('/users/<int:user_id>/feedback', methods=['GET'])
@jwt_required()
def get_user_feedback(user_id):
    """获取用户的推荐反馈历史"""
    try:
        current_user_id = int(get_jwt_identity())
        current_user = User.query.get(current_user_id)
        
        # 验证权限
        if current_user.role != 'admin' and current_user_id != user_id:
            return jsonify({
                'success': False,
                'error': 'Permission denied'
            }), 403
        
        feedbacks = RecommendationFeedback.query.filter_by(
            user_id=user_id
        ).order_by(RecommendationFeedback.created_at.desc()).all()
        
        feedback_data = []
        for fb in feedbacks:
            fb_dict = fb.to_dict()
            if fb.course:
                fb_dict['course'] = fb.course.to_dict()
            feedback_data.append(fb_dict)
        
        return jsonify({
            'success': True,
            'data': feedback_data,
            'total': len(feedback_data)
        }), 200
        
    except Exception as e:
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500

